/**
 * 树形数据处理
 * tree-tool
 */

const DEFAULT_CONFIG = {
    id: 'id',
    children: 'children',
    pid: 'pid'
}

const getConfig = config => Object.assign({}, DEFAULT_CONFIG, config)

/**
 * 列表结构转树结构
 * @param {array} list 列表数据
 * @param {object} config 键名配置
 * @returns 
 */
export function toTree(list, config = {}) {
    config = getConfig(config)
    const nodeMap = new Map(), result = [], { id, children, pid } = config
    // 第一遍循环使用对象将每项添加一个 children
    for (const node of list) {
        node[children] = node[children] || []
        nodeMap.set(node[id], node)
    }
    // 第二遍循环将【每项放到对应的父级下】，如果没有则父级放到result
    for (const node of list) {
        const parent = nodeMap.get(node[pid]);
        if (parent) {
            parent[children].push(node)
        } else {
            result.push(node)
        }
    }
    return result
}

/**
 * 树结构转列表结构
 * @param {array} tree 树状数据
 * @param {object} config 键名配置
 * @returns 
 */
export function toList(tree, config = {}) {
    config = getConfig(config)
    // 先放入第一层
    const { children } = config, result = [...tree]
    for (let i = 0; i < result.length; i++) {
        if (!result[i][children]) continue
        result.splice(i + 1, 0, ...result[i][children])
        delete result[i][children]
    }
    return result
}

/**
 * 查询节点路径
 * @param {array} tree 
 * @param {function} func 
 * @param {object} config 
 * @returns 
 */
export function treeFindPath(tree, func, config = {}, path = []) {
    config = getConfig(config)
    if (!tree) return []
    for (const data of tree) {
        path.push(data[config.id])
        if (func(data)) return path
        if (data[config.children]) {
            const findChildren = treeFindPath(data[config.children], func, config, path)
            if (findChildren.length) return findChildren
        }
        path.pop()
    }
    return []
}

/**
 * 查找符合条件的单个节点
 * @param {array} tree 树型数据
 * @param {function} func 判断函数
 * @param {any} config 配置参数
 * @returns 
 */
export function findNode(tree, func, config = {}) {
    config = getConfig(config)
    const { children } = config, list = [...tree]
    for (let node of list) {
        if (func(node)) return node
        node[children] && list.push(...node[children])
    }
    return null
}


/**
 * 查找符合条件的所有节点
 * @param {array} tree 树型数据
 * @param {function} func 判断函数
 * @param {any} config 配置参数
 * @returns 
 */
export function findNodeAll(tree, func, config = {}) {
    config = getConfig(config)
    const { children } = config, list = [...tree], result = []
    for (let node of list) {
        func(node) && result.push(node)
        node[children] && list.push(...node[children])
    }
    return result
}

/**
 * 遍历树形数据
 * @param {array} tree 
 * @param {function} fn 
 */
export function readTree(tree, fn) {
    for (let item of tree) {
        fn(item)
        if (item.children && item.children.length) {
            readTree(item.children, fn)
        }
    }
}